#include "fileserver.h"
#include <QWebSocketServer>
#include <QWebSocket>
#include <QJsonDocument>
#include <QJsonObject>
#include <QtConcurrent>
#include <QCryptographicHash>
FileServer::FileServer(QObject * parent)
    : QObject(parent),
      m_pWebSocketServer(new QWebSocketServer(QStringLiteral("File Server"),
                                              QWebSocketServer::NonSecureMode, this)),
      m_client(Q_NULLPTR)

{
    m_offset = 0;
}

FileServer::~FileServer()
{
    m_waitCondition.wakeAll();
    QThreadPool::globalInstance()->waitForDone();

    m_pWebSocketServer->close();
    qDebug() << "file server deleted";
}

void FileServer::onNewConnection()
{
    QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();

    if (m_client)
    {
        pSocket->close();
        pSocket->deleteLater();
    }

    connect(pSocket, &QWebSocket::textMessageReceived, this, &FileServer::processTextMessage);
    connect(pSocket, &QWebSocket::disconnected, this, &FileServer::socketDisconnected);

    m_client = pSocket;
}

void FileServer::processTextMessage(const QString &message)
{
   // QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());

    QJsonDocument document = QJsonDocument::fromJson(message.toUtf8());
    QJsonObject object = document.object();

    QString status = object.value("status").toString();

    qDebug() << "server received:" << message;

    if (status == "begin") {
        qint64 pos = object.value("pos").toInt();

        m_offset = 0;
        if (m_file->seek(pos))
            QtConcurrent::run(this, &FileServer::sendBinaryData);
        else
        {
           notifyError("seek pos error");
        }
    }
    else if (status == "continue")
    {
        qDebug() << "continue>>>>>";
        m_waitCondition.wakeOne();
    }
    else if (status == "repeat")
    {
       notifyTransport();
    }
    else if (status == "finish")
    {
        if (m_pWebSocketServer->isListening()) {
            m_pWebSocketServer->close();
        }
    }
}

void FileServer::socketDisconnected()
{
    QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());

    qDebug() << "socketDisconnected:" << pClient;

    if (pClient) {
        pClient->deleteLater();
        m_client = Q_NULLPTR;
    }
    m_waitCondition.wakeAll();

    if (m_pWebSocketServer->isListening()) {
        m_pWebSocketServer->close();
    }
}

//void FileServer::processBinaryMessage(const QByteArray &message)
//{
//    qint64 size = m_client->sendBinaryMessage(message);
//    emit this->progress(size, m_size);
//    qDebug() << "send size: " << size;
//}

void FileServer::processTimerCheckConnected()
{
    if (m_client.isNull())
    {
        if (m_pWebSocketServer->isListening())
            m_pWebSocketServer->close();
    }
}

void FileServer::notifyTransport()
{
    QJsonObject object;
    object.insert("status", "transport");
    object.insert("crc", m_crc);
    object.insert("size", m_buffer.size());
    object.insert("data", QString(m_buffer.toBase64()));
    m_client->sendTextMessage(QJsonDocument(object).toJson());
    emit this->progress(m_buffer.size(), m_size);
}

void FileServer::notifyError(const QString &error)
{
    QJsonObject object;
    object.insert("status", "error");
    object.insert("text", error);
    m_client->sendTextMessage(QJsonDocument(object).toJson());
}

void FileServer::sendBinaryData()
{
    while(!m_file->atEnd())
    {
        m_buffer = m_file->read(1024 * 16);

        m_offset += m_buffer.size();
        m_crc = qChecksum(m_buffer, m_buffer.length());

        QMetaObject::invokeMethod(this, "notifyTransport", Qt::BlockingQueuedConnection);


         QWriteLocker locker(&m_lock);

        m_waitCondition.wait(&m_lock);


    }
}
qint64 FileServer::size() const
{
    return m_size;
}

void FileServer::setSize(const qint64 &size)
{
    m_size = size;
}

QString FileServer::token() const
{
    return m_token;
}

void FileServer::setFileName(const QString &fileName)
{
    m_file = new QFile(fileName, this);
    m_token = QCryptographicHash::hash(fileName.toUtf8(), QCryptographicHash::Md5).toHex();
    m_file->open(QIODevice::ReadOnly);
}

void FileServer::listen()
{
    if (m_pWebSocketServer->listen(QHostAddress::Any)) {
        qDebug() << "Echoserver listening on port" << m_pWebSocketServer->serverPort();
        connect(m_pWebSocketServer, &QWebSocketServer::newConnection, this, &FileServer::onNewConnection);
        connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &FileServer::closed);

        QTimer::singleShot(60000, this, &FileServer::processTimerCheckConnected);
    }

}

quint16 FileServer::port() const
{
    return m_pWebSocketServer->serverPort();
}

